home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-01
/
phmacro.zip
/
MAC.C
< prev
next >
Wrap
Text File
|
1993-01-04
|
16KB
|
492 lines
/* macros - skeleton of macro expansion algorithm
* Each token is one char (letter a-z).
* Token-buffers are modeled by plain old char strings.
* Each such buffer has another same-size buffer printed just under it,
* which shows the "hide-set" associated with the token above it.
* (The "hide-set" contains all the tokens that are non-replaceable.)
* #define is #d (no space before or after #).
* No attempt to model details of whitespace handling.
* Two tokens, when catenated, produce "second token plus one".
* "Stringize" is crudely stubbed; two quotes "" replace the string.
* Does not handle empty actuals gracefully.
* Does not span newlines in matching actuals.
* Revisions:
* 88/10/01: Identify the "replacement" buffer as "R".
* 88/10/03: Handle the hide-set of actuals properly.
* 88/10/03: Indicate where to refine for the exact (unspecified)
* semantics of interactions of hide-sets in catenation
*/
/*
* First version written by Thomas Plum, Plum Hall Inc.
* Subsequently revised by members of X3J11, the
* ANSI C Standards Committee.
* Permission is granted to reproduce and use this program,
* for all purposes, provided that this notice is included.
*/
/* First, 300+ lines of inelegant support routines, to about 363 ... */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define TRACE(x) 0 /* or, printf x , if needed for debugging */
#define OUT(x) printf x
#define cpy_nam(p, q) (p[0] = *(q), p[1] = '\0', ++(q))
#define eobuf(p) (*(p) == '\0')
#define next(p, s) (strncmp(p, s, (length_matched = strlen(s))) == 0)
#define advance(p) (p += length_matched)
#define is_obj(p) in_set(obj.nam, *(p))
#define is_fn(p) in_set(fn.nam, *(p))
#define obj_def(p) obj.def[obj_num(p)][0]
#define obj_num(p) (strchr(obj.nam, *(p)) - obj.nam)
#define fn_def(p) fn.def[fn_num(p)][0]
#define fn_num(p) (strchr(fn.nam, *(p)) - fn.nam)
#define fn_parm_count(p) strlen(fn.parms[fn_num(p)])
#define fn_parm_index(p, q) \
(strchr(fn.parms[fn_num(p)], *(q)) - fn.parms[fn_num(p)])
#define is_parm_name(p, q) in_set(fn.parms[fn_num(p)], *(q))
#define in_set(p, c) (strchr(p, c) != 0)
#define hide_set(p) *((char*)p + L)
#define A 9 /* max # of macro args */
#define D 26 /* max # of macro defs */
#define L 100 /* max length of an input line or of a macro def */
int length_matched;
void diagram(), expand(), expand_fn(), set_hide(), listcpy();
void add_hide(), lower_case();
char *h_set();
char arg_patterns[10][24] =
{
"()",
"(_)",
"(_,_)",
"(_,_,_)",
"(_,_,_,_)",
"(_,_,_,_,_)",
"(_,_,_,_,_,_)",
"(_,_,_,_,_,_,_)",
"(_,_,_,_,_,_,_,_)",
"(_,_,_,_,_,_,_,_,_)",
};
char next_token(p) char *p;
{
length_matched = 0;
++p;
for (;;)
{
if (*p == ' ')
++p, ++length_matched;
else if (*p == '\0')
{
printf("treating end-of-buffer as end-of-file\n");
return *p;
}
else
return *p;
}
}
void strip_blanks(p) char *p;
{
char *q = p;
while (*p == ' ')
++p;
/* now at first non-blank in p */
while (*p != '\0') /* can't use strcpy because overlap */
hide_set(q) = hide_set(p), *q++ = *p++;
while (*--q == ' ')
;
*++q = '\0', hide_set(q) = '\0';
}
struct obj { int n; char nam[D]; char def[D][2][L]; }
obj = {0};
struct fn { int n; char nam[D]; char def[D][2][L]; char parms[D][A]; }
fn = {0};
void install_obj(nam, def) char *nam; char *def;
{
obj.nam[obj.n] = nam[0];
strcpy(obj.def[obj.n][0], def);
strip_blanks(obj.def[obj.n][0]);
set_hide(obj.def[obj.n][0], nam);
printf("obj: nam=<%s> def=<%s>\n", nam, obj.def[obj.n][0]);
printf(" <%s>\n", obj.def[obj.n][1]);
++obj.n;
}
void install_fn(nam, def, parms) char *nam; char *def; char *parms;
{
fn.nam[fn.n] = nam[0];
strcpy(fn.def[fn.n][0], def);
strcpy(fn.parms[fn.n], parms);
strip_blanks(fn.def[fn.n][0]);
set_hide(fn.def[fn.n][0], nam);
printf("fn: nam=<%s> parms=<%s> def=<%s>\n", nam, parms, fn.def[fn.n][0]);
printf(" %*s <%s>\n",
strlen(parms), "", fn.def[fn.n][1]);
++fn.n;
}
char *parse_parms(p, parms) char *p; char *parms;
{
int i = 0;
while (*p != ')')
{
if (*p != ' ' && *p != ',')
parms[i++] = *p;
++p;
}
++p;
parms[i] = '\0';
return p;
}
void replace(level, suf, buf, p, p2, def) char *level, *suf, *buf, *p, *p2, *def;
{
char hold[2][L];
char old_hide[2];
listcpy(hold[0], p2);
old_hide[0] = hide_set(p), old_hide[1] = '\0';
listcpy(p, def);
add_hide(p, old_hide);
listcpy(p + strlen(def), hold[0]);
diagram(level, buf, suf);
}
char *match_actuals(p, actual, n) char *p; char actual[A][2][L]; int n;
{
int parens = 1;
int i = 0;
int j = 0;
TRACE(("match_actuals(<%s>, actual)\n", p));
p += 1;
/* past the '(' */
for ( ;; )
{
if (*p == '(')
{
++parens;
actual[i][1][j] = p[L];
actual[i][0][j++] = *p++;
}
else if (*p == ')')
{
--parens;
if (parens == 0)
break;
actual[i][1][j] = p[L];
actual[i][0][j++] = *p++;
}
else if (*p == ',' && parens == 1)
{
actual[i][1][j] = '\0';
actual[i][0][j] = '\0';
j = 0;
++i;
++p;
}
else
{
actual[i][1][j] = p[L];
actual[i][0][j++] = *p++;
}
}
actual[i][1][j] = '\0';
actual[i][0][j] = '\0';
/* should strip blanks on each actual, to be sure non-empty */
if (i == 0 && n == 0)
; /* ok, no args */
else if (i != n-1)
{
printf("wrong number of actuals\n");
exit(2);
}
for (i = 0; i < n; ++i)
TRACE(("actual[%d] = <%s>\n", i, actual[i][0]));
return p+1;
}
char *stringize(s) char *s;
{
static char string_buf[2][L] = {"\"\"", " "};
printf("string \"%s\" --> \"\"\n", s);
return string_buf[0];
}
char *catenate(p, q) char *p, *q;
{
static char cat_buf[2][L];
char old_hide[2];
cat_buf[0][0] = q[0] + 1;
cat_buf[0][1] = '\0';
set_hide(cat_buf[0], " ");
old_hide[0] = hide_set(p), old_hide[1] = '\0';
add_hide(cat_buf[0], old_hide);
old_hide[0] = hide_set(q), old_hide[1] = '\0';
add_hide(cat_buf[0], old_hide); /* NOTE: The other "unspecified" */
/* choice is to intersect(!) the */
/* two hide-sets, as in Prosser */
/* 86-196. */
printf("catenate %c%c --> %c\n", p[0], q[0], cat_buf[0][0]);
return cat_buf[0];
}
void listcpy(p, q) char *p, *q;
{
strcpy(p, q);
strcpy(&hide_set(p), &hide_set(q));
}
void diagram(level, s, suf) char *level, *s, *suf;
{
char prefix[L];
if (level[0] == '\0')
strcpy(prefix, "0");
else
strcpy(prefix, level+1);
printf("%s%s: %s\n", prefix, suf, s);
printf("%*s%.*s\n", strlen(prefix)+strlen(suf)+2, ": ",
strlen(s), &hide_set(s));
}
int charcmp(s, t) char *s, *t;
{
return *s - *t;
}
void set_hide(def, nam) char *def, *nam;
{
memset(&hide_set(def), nam[0], strlen(def));
def[L+strlen(def)] = '\0';
}
char hide_sets[10][10] = {0};
int n_hide_sets = 0;
void add_hide(p, h) char *p, *h;
{
int i, lim;
lim = strlen(p);
for (i = 0; i < lim; ++i)
{
char c = p[L+i];
char old_h[2];
old_h[0] = c, old_h[1] = '\0';
TRACE(("c=<%c>, h=<%c>\n", c, h[0]));
if (h[0] == ' ')
;
else if (c == ' ')
p[L+i] = h[0];
else if (h[0] == c)
;
else if (in_set(h_set(old_h), h[0]))
;
else
{
char new_hide[10];
int n = n_hide_sets;
int j;
int found = 0;
strcpy(new_hide, h_set(old_h));
strcat(new_hide, h_set(h));
qsort(new_hide, strlen(new_hide), 1, charcmp);
TRACE(("new_hide=<%s>\n", new_hide));
for (j = 0; j < n_hide_sets; ++j)
{
if (strcmp(new_hide, hide_sets[j]) == 0)
{
p[L+i] = j + '0';
found = 1;
}
}
if (!found)
{
if (n > 9)
{
printf("too many hide-sets\n");
exit(2);
}
strcpy(hide_sets[n], new_hide);
p[L+i] = n + '0';
qsort(hide_sets[n], strlen(hide_sets[n]), 1, charcmp);
printf("hide-set #%d = {%s}\n", n, hide_sets[n]);
++n_hide_sets;
}
}
}
}
char *h_set(s) char *s;
{
char c = s[0];
if (isdigit(c))
return hide_sets[c - '0'];
else
return s;
}
int is_hidden(p) char *p;
{
if (!islower(*p))
return 0;
else if (*p == p[L])
return 1;
else if (isdigit(p[L]) && strchr(hide_sets[p[L] - '0'], *p) != 0)
return 1;
else
return 0;
}
void mark_non_replace(p) char *p;
{
*p = toupper(*p);
}
void lower_case(p) char *p;
{
for ( ; *p != '\0'; ++p)
*p = tolower(*p);
}
/* Now: Here's the actual macro algorithm ... */
void preproc(p) char *p;
{
char nam[2];
char parms[A];
if (next(p, "#d "))
{ /* starting a #define */
advance(p);
TRACE(("p=<%s>\n", p));
cpy_nam(nam, p);
if (next(p, "(")) /* a fn-like macro */
{
advance(p);
p = parse_parms(p, parms);
install_fn(nam, p, parms);
}
else /* an object-like macro */
install_obj(nam, p);
} /* end of #define */
else if (next(p, "#u "))
{ /* starting a #undef */
} /* stub */
else
{
expand(p, "");
lower_case(p);
set_hide(p, " ");
diagram("", p, "");
}
}
void expand(buf, level) char *buf; char *level;
{
char *p = buf;
TRACE(("expand(<%s>, %s)\n", buf, level));
diagram(level, buf, "");
while (!eobuf(p))
{
if (is_hidden(p))
{
mark_non_replace(p);
++p;
diagram(level, buf, "");
}
else if (is_obj(p)) /* instance of object-like macro */
replace(level, "", buf, p, p+1, obj_def(p));
else if (is_fn(p) && next_token(p) == '(')
expand_fn(buf, p, level); /* instance of fn-like macro */
else
++p; /* ordinary token */
} /* end while !eobuf */
} /* end expand() */
void expand_fn(buf, p, level) char *buf, *p, *level;
{
char actual[A][2][L], expandeds[A][2][L];
char repl[2][L];
char nlevel[20];
char fn_nam[2];
char invocation[2][L];
char *start_invok, *q;
int i_parm, num_parms;
start_invok = p;
cpy_nam(fn_nam, p);
advance(p); /* past any blanks skipped in next_token */
num_parms = fn_parm_count(fn_nam);
p = match_actuals(p, actual, num_parms);
for (i_parm = 0; i_parm < num_parms; ++i_parm)
{
listcpy(expandeds[i_parm][0], actual[i_parm][0]);
sprintf(nlevel, "%s.%d", level, i_parm+1);
expand(expandeds[i_parm][0], nlevel);
}
sprintf(invocation[0], "%s%s", fn_nam, arg_patterns[num_parms]);
set_hide(invocation[0], " ");
diagram(level, invocation[0], "R");
listcpy(repl[0], fn_def(fn_nam));
diagram(level, repl[0], "R");
TRACE(("subst parms in repl:<%s>\n", repl));
for (q = repl[0]; !eobuf(q); )
{
TRACE(("repl-token <%c>\n", *q));
if (q[1] == '#' && q[2] == '#' && !eobuf(q+3))
{
replace(level, "R", repl[0], q, q+4,
catenate(q, q+3));
q += 1; /* advance past new "token" */
}
else if (q[0] == '#' && is_parm_name(fn_nam, q+1))
{
i_parm = fn_parm_index(fn_nam, q+1);
replace(level, "R", repl[0], q, q+2,
stringize(actual[i_parm][0]));
q += 2; /* advance past "" */
}
else if (is_parm_name(fn_nam, q))
{
i_parm = fn_parm_index(fn_nam, q);
replace(level, "R", repl[0], q, q+1,
expandeds[i_parm][0]);
q += strlen(expandeds[i_parm][0]); /* advance past expansion */
}
else /* ordinary token */
++q;
}
replace(level, "", buf, start_invok, p, repl[0]);
}
main()
{
char line[BUFSIZ];
while (gets(line))
{
set_hide(line, " ");
preproc(line);
}
} /* end main */